﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace Windy.DragonCurve.Views
{
    /// <summary>
    /// Interaction logic for DragonCurveImage.xaml
    /// </summary>
    public partial class DragonCurveView : UserControl
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        public DragonCurveView()
        {
            InitializeComponent();
            DragonCurvePath = new Path();
            PathContainer.Content = DragonCurvePath;
            DragonCurve = new DragonCurve();
            DragonCurve.PropertyChanged += new PropertyChangedEventHandler(DragonCurve_PropertyChanged);
        }

        public DragonCurve DragonCurve { get; private set; }
        public Path DragonCurvePath { get; private set; }

        #region Dependency Property DragonCurveScheme

        public DragonCurveScheme DragonCurveScheme
        {
            get { return (DragonCurveScheme)GetValue(DragonCurveSchemeProperty); }
            set { SetValue(DragonCurveSchemeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DragonCurveScheme.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DragonCurveSchemeProperty = DependencyProperty.Register("DragonCurveScheme", typeof(DragonCurveScheme), typeof(DragonCurveView), new UIPropertyMetadata(null, OnDragonCurveSchemeChanged));

        public static void OnDragonCurveSchemeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
            var scheme = e.NewValue as DragonCurveScheme;
            c.DragonCurve.ConfigureCurve(scheme);
            c.OnDragonCurveSchemeChanged(e);
        }

        public virtual void OnDragonCurveSchemeChanged(DependencyPropertyChangedEventArgs e)
        {
            if (DragonCurveSchemeChanged != null)
                DragonCurveSchemeChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler DragonCurveSchemeChanged;

        #endregion

        #region Dependency Property StepLength

        public double StepLength
        {
            get { return (double)GetValue(StepLengthProperty); }
            set { SetValue(StepLengthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StepLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StepLengthProperty = DependencyProperty.Register("StepLength", typeof(double), typeof(DragonCurveView), new UIPropertyMetadata(10.0d, OnStepLengthChanged));

        public static void OnStepLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
            c.DragonCurve.StepLength = (double)e.NewValue;
			c.DragonCurve.Refresh();
            c.OnStepLengthChanged(e);
        }

        private void OnDragonCurveStepLengthChanged()
        {
            if (DragonCurve.StepLength != this.StepLength)
                this.StepLength = DragonCurve.StepLength;
        }

        public virtual void OnStepLengthChanged(DependencyPropertyChangedEventArgs e)
        {
            if (StepLengthChanged != null)
                StepLengthChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler StepLengthChanged;

        #endregion

        #region Dependency Property CurveBrush

        public Brush CurveBrush
        {
            get { return (Brush)GetValue(CurveBrushProperty); }
            set { SetValue(CurveBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurveBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurveBrushProperty = DependencyProperty.Register("CurveBrush", typeof(Brush), typeof(DragonCurveView), new UIPropertyMetadata(Brushes.Black, OnCurveBrushChanged));

        public static void OnCurveBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
			c.DragonCurve.Refresh();
            c.OnCurveBrushChanged(e);
        }

        public virtual void OnCurveBrushChanged(DependencyPropertyChangedEventArgs e)
        {
            if (CurveBrushChanged != null)
                CurveBrushChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler CurveBrushChanged;

        #endregion

        #region Dependency Property CurveThickness

		private double curveThickness;
        public double CurveThickness
        {
            get { return curveThickness; }
            set { SetValue(CurveThicknessProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurveThickness.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurveThicknessProperty = DependencyProperty.Register("CurveThickness", typeof(double), typeof(DragonCurveView), new UIPropertyMetadata(1.0D, OnCurveThicknessChanged));

        public static void OnCurveThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
			c.curveThickness = (double)e.NewValue;
			c.DragonCurve.Refresh();
            c.OnCurveThicknessChanged(e);
        }

        public virtual void OnCurveThicknessChanged(DependencyPropertyChangedEventArgs e)
        {
            if (CurveThicknessChanged != null)
                CurveThicknessChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler CurveThicknessChanged;

        #endregion

        #region Dependency Property StartPointX

        private double startPointX;
        public double StartPointX
        {
            get { return startPointX; }
            set { SetValue(StartPointXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartPointX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartPointXProperty = DependencyProperty.Register("StartPointX", typeof(double), typeof(DragonCurveView), new UIPropertyMetadata(50.0, OnStartPointXChanged));

        public static void OnStartPointXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
            c.startPointX = (double)e.NewValue;
            c.SaveStartPoint();
            c.OnStartPointXChanged(e);
        }

        public virtual void OnStartPointXChanged(DependencyPropertyChangedEventArgs e)
        {
            if (StartPointXChanged != null)
                StartPointXChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler StartPointXChanged;

        #endregion

        #region Dependency Property StartPointY

        private double startPointY;
        public double StartPointY
        {
            get { return startPointY; }
            set { SetValue(StartPointYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartPointY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartPointYProperty = DependencyProperty.Register("StartPointY", typeof(double), typeof(DragonCurveView), new UIPropertyMetadata(50.0, OnStartPointYChanged));

        public static void OnStartPointYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c = d as DragonCurveView;
            c.startPointY = (double)e.NewValue;
            c.SaveStartPoint();
            c.OnStartPointYChanged(e);
        }

        public virtual void OnStartPointYChanged(DependencyPropertyChangedEventArgs e)
        {
            if (StartPointYChanged != null)
                StartPointYChanged(this, e);
        }

        public event DependencyPropertyChangedEventHandler StartPointYChanged;

        #endregion

        #region Command Bindings

        private void DragonCurveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = DragonCurve.IsInitialized;
            logger.Info("Can DragonCurveCommand Execute: {0}", e.CanExecute);
            e.Handled = true;
        }

        private void UpdateDragonCurveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DragonCurve.Update();
        }

        private void ResetDragonCurveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DragonCurve.Reset();
        }

        #endregion

        public void DragonCurve_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "StepLength":
                    OnDragonCurveStepLengthChanged();
                    break;
                case "Geometry":
                    UpdateCurve();
                    break;
                case "StartPoint":
                    UpdateStartPoint();
                    break;
                default:
                    break;
            }
        }

        private void UpdateStartPoint()
        {
            if (this.StartPointX != DragonCurve.StartPoint.X)
                this.StartPointX = DragonCurve.StartPoint.X;

            if (this.StartPointY != DragonCurve.StartPoint.Y)
                this.startPointY = DragonCurve.StartPoint.Y;
        }
        private void SaveStartPoint()
        {
            if (this.StartPointX != DragonCurve.StartPoint.X ||
                this.StartPointY != DragonCurve.StartPoint.Y)
			{
                DragonCurve.StartPoint = new Point(this.StartPointX, this.StartPointY);
				DragonCurve.Refresh();
			}
        }

        private void UpdateCurve()
        {
            DragonCurvePath.Data = DragonCurve.Geometry;
        }
    }
}
